#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/msticker.h>
#include <mutex>
#include <ohaudio/native_audio_routing_manager.h>
#include <ohaudio/native_audiocapturer.h>
#include <ohaudio/native_audiostream_base.h>
#include <ohaudio/native_audiostreambuilder.h>
#include <queue>

struct AudioDeviceInfo {
	uint32_t id;
	std::string name;
	uint32_t inputCount = 0;
	uint32_t outputCount = 0;
	uint32_t supportedClockRates{0};
	uint32_t *supportedChannelCounts;
};

std::string AudioDeviceTypeToString(OH_AudioDevice_Type type) {
	switch (type) {
		case AUDIO_DEVICE_TYPE_INVALID:
			return "Invalid";
		case AUDIO_DEVICE_TYPE_EARPIECE:
			return "Earpiece";
		case AUDIO_DEVICE_TYPE_SPEAKER:
			return "Speaker";
		case AUDIO_DEVICE_TYPE_WIRED_HEADSET:
			return "Headset";
		case AUDIO_DEVICE_TYPE_WIRED_HEADPHONES:
			return "Wired headphones";
		case AUDIO_DEVICE_TYPE_BLUETOOTH_SCO:
			return "Bluetooth SCO";
		case AUDIO_DEVICE_TYPE_BLUETOOTH_A2DP:
			return "Bluetooth A2DP";
		case AUDIO_DEVICE_TYPE_MIC:
			return "Microphone";
		case AUDIO_DEVICE_TYPE_USB_HEADSET:
			return "USB headset";
		case AUDIO_DEVICE_TYPE_DISPLAY_PORT:
			return "Display port";
		case AUDIO_DEVICE_TYPE_REMOTE_CAST:
			return "Remote cast";
		case AUDIO_DEVICE_TYPE_DEFAULT:
			return "Default";
		default:
			break;
	}
	return "Unspecified";
}

class AudioDeviceManager {
public:
	static std::vector<AudioDeviceInfo> GetDevices() {
		std::vector<AudioDeviceInfo> result;
		OH_AudioRoutingManager *audioRoutingManager = nullptr;
		OH_AudioCommon_Result ret = OH_AudioManager_GetAudioRoutingManager(&audioRoutingManager);
		if (ret != AUDIOCOMMON_RESULT_SUCCESS) {
			ms_message("Failed to get OH_AudioRoutingManager from ohos");
			return result;
		}
		OH_AudioDeviceDescriptorArray *audioDeviceDescriptorArray = nullptr;
		ret =
		    OH_AudioRoutingManager_GetDevices(audioRoutingManager, AUDIO_DEVICE_FLAG_ALL, &audioDeviceDescriptorArray);
		if (ret != AUDIOCOMMON_RESULT_SUCCESS) {
			ms_message("Failed to get audio Devices from ohos");
			return result;
		}
		for (uint32_t index = 0; index < audioDeviceDescriptorArray->size; index++) {
			AudioDeviceInfo device;

			OH_AudioDeviceDescriptor *descriptor = audioDeviceDescriptorArray->descriptors[index];

			uint32_t deviceId = 0;
			ret = OH_AudioDeviceDescriptor_GetDeviceId(descriptor, &deviceId);
			if (ret == AUDIOCOMMON_RESULT_SUCCESS) {
				ms_message("get audio device id success");
				device.id = deviceId;
			}

			char *name = nullptr;
			ret = OH_AudioDeviceDescriptor_GetDeviceName(descriptor, &name);
			if (ret == AUDIOCOMMON_RESULT_SUCCESS) {
				ms_message("get audio device name success");
				device.name = name;
			}
			uint32_t size;
			uint32_t *rates;
			ret = OH_AudioDeviceDescriptor_GetDeviceSampleRates(descriptor, &rates, &size);
			if (ret == AUDIOCOMMON_RESULT_SUCCESS) {
				device.supportedClockRates = rates[0];
				ms_message("get audio rates success,rates:%d", rates[0]);
				ms_message("get audio rates success,supportedClockRates:%d", device.supportedClockRates);
			}
			uint32_t *supportedChannelCounts;
			ret = OH_AudioDeviceDescriptor_GetDeviceChannelCounts(descriptor, &supportedChannelCounts, &size);
			if (ret == AUDIOCOMMON_RESULT_SUCCESS) {
				ms_message("get audio channel Count success");
				device.supportedChannelCounts = supportedChannelCounts;
			}

			OH_AudioDevice_Role role = AUDIO_DEVICE_ROLE_INPUT;
			ret = OH_AudioDeviceDescriptor_GetDeviceRole(descriptor, &role);
			if (ret == AUDIOCOMMON_RESULT_SUCCESS) {
				ms_message("get audio role success");
				if (role == AUDIO_DEVICE_ROLE_INPUT) {
					device.inputCount++;
				} else if (role == AUDIO_DEVICE_ROLE_OUTPUT) {
					device.outputCount++;
				}
			}
			OH_AudioDevice_Type type = AUDIO_DEVICE_TYPE_INVALID;
			ret = OH_AudioDeviceDescriptor_GetDeviceType(descriptor, &type);
			if (ret == AUDIOCOMMON_RESULT_SUCCESS) {
				ms_message("get audio type success");
			}
			if (device.name.length() == 0) {
				device.name = AudioDeviceTypeToString(type) + " (" + std::to_string(device.id) + ")";
			}
			result.push_back(device);
		}

		OH_AudioRoutingManager_ReleaseDevices(audioRoutingManager, audioDeviceDescriptorArray);
		return result;
	}
};

class AudioRenderer;

struct OHOSAudioWriteData {
	OHOSAudioWriteData() {
		stype = 0;
		rate = 48000;
		nchannels = 1;
		nFramesRequested = 0;
		ms_mutex_init(&mutex, NULL);
		ms_bufferizer_init(&bf);
	}
	~OHOSAudioWriteData() {
		ms_mutex_destroy(&mutex);
		ms_bufferizer_uninit(&bf);
	}

	void updateStreamTypeFromMsSndCard() {
		// MSSndCardStreamType type = ms_snd_card_get_stream_type(soundCard);
		// stype = 0;
		// if (type == MS_SND_CARD_STREAM_RING) {
		// 	ms_message("Sound card configured for RING stream");
		// 	stype = 2;
		// } else if (type == MS_SND_CARD_STREAM_MEDIA) {
		// 	ms_message("Sound card configured for MEDIA stream");
		// 	stype = 3;
		// } else if (type == MS_SND_CARD_STREAM_DTMF) {
		// 	ms_message("Sound card configured for DTMF stream");
		// 	stype = 8;
		// } else {
		// 	ms_message("Sound card configured for VOICE stream");
		// }
	}
	MSSndCard *soundCard;
	int stype;
	MSBufferizer bf;
	AudioRenderer *tr;
	int rate;
	int nchannels;
	int nbufs;
	int nFramesRequested;
	bool mStarted;
	uint64_t flowControlStart;
	int minBufferFilling;
	ms_mutex_t mutex;
};

class Buffer {
public:
	size_t frameCount; // number of sample frames corresponding to size;
	                   // on input it is the number of frames desired,
	                   // on output is the number of frames actually filled

	size_t size; // input/output in byte units
	union {
		void *raw;
		short *i16; // signed 16-bit
		int8_t *i8; // unsigned 8-bit, offset by 0x80
	};
};

class AudioRenderer {
private:
	uint32_t sampleRate;
	uint32_t channelCount;
	uint32_t frameSize;
	std::mutex mtx_;
	OHOSAudioWriteData *ad_{nullptr};
	std::queue<std::string> buffers_;
	OH_AudioRenderer *renderer_{nullptr};
	OH_AudioStreamBuilder *builder{nullptr};

public:
	AudioRenderer(uint32_t rate, uint32_t channel_count, uint32_t frame_size, OHOSAudioWriteData *addd)
	    : sampleRate(rate), channelCount(channel_count), frameSize(frame_size), ad_(addd) {
		initAudioRenderer();
	}

	static int32_t OnWriteData_1(OH_AudioRenderer *renderer, void *userData, void *buffer, int32_t length) {
		auto self = (AudioRenderer *)userData;
		return self->OnWriteData(renderer, buffer, length);
	}

	static int32_t OnStreamEvent_1(OH_AudioRenderer *renderer, void *userData, OH_AudioStream_Event event) {
		auto self = (AudioRenderer *)userData;
		return self->OnStreamEvent(renderer, event);
	}

	static int32_t OnInterruptEvent_1(OH_AudioRenderer *renderer,
	                                  void *userData,
	                                  OH_AudioInterrupt_ForceType type,
	                                  OH_AudioInterrupt_Hint hint) {
		auto self = (AudioRenderer *)userData;
		return self->OnInterruptEvent(renderer, type, hint);
	}

	static int32_t OnError_1(OH_AudioRenderer *renderer, void *userData, OH_AudioStream_Result error) {
		auto self = (AudioRenderer *)userData;
		return self->OnError(renderer, error);
	}

	// 函数用于将void *数组转换为int16_t数组
	void convertVoidToInt16(void *buffer, int bufferSize, int16_t **result, size_t *resultSize) {
		*resultSize = bufferSize / sizeof(int16_t);
		*result = (int16_t *)malloc(*resultSize * sizeof(int16_t));
		if (*result == NULL) {
			return;
		}
		// 使用memcpy进行内存复制
		memcpy(*result, buffer, bufferSize);
	}

	void SetBuffer(MSFilter *obj) {
		std::unique_lock<std::mutex> lock(mtx_);
		mblk_t *m;
		while ((m = ms_queue_get(obj->inputs[0])) != NULL) {
			mblk_t *o;
			msgpullup(m, -1);
			o = allocb((m->b_wptr - m->b_rptr) * 2, 0);
			mblk_meta_copy(m, o);
			buffers_.emplace(std::string((char *)m->b_rptr, (size_t)(m->b_wptr - m->b_rptr)));
			freemsg(m);
		}
	}

	void readBuffer(const void *p_info, Buffer *buffer) {
		*buffer = *(const Buffer *)p_info;
	}

	void writeBuffer(void *p_info, const Buffer *buffer) {
		*(Buffer *)p_info = *buffer;
	}

	int32_t OnWriteData(OH_AudioRenderer *renderer, void *buffer, int32_t length) {
		(void)renderer;
		int64_t position;
		int64_t timestamp;

		auto result = OH_AudioRenderer_GetTimestamp(renderer_, CLOCK_MONOTONIC, &position, &timestamp);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioRenderer Ohos stream GetTimestamp fail");
			return -1;
		}
		std::unique_lock<std::mutex> lock(mtx_);
		if (!ad_) {
			ms_message("AudioRenderer OnWriteData ad_ is null ");
			return -1;
		}
		// Buffer info;
		int avail;
		int ask;
		// readBuffer(buffer, &info);
		ms_mutex_lock(&ad_->mutex);
		ask = length;
		avail = ms_bufferizer_get_avail(&ad_->bf);
		ms_message("AudioRenderer avail is:%d ,length:%d", avail, length);
		if (avail < length) {
			ms_mutex_unlock(&ad_->mutex);
			return -1;
		}

		if ((ad_->nbufs == 0) && (avail > (ask * 2))) {
			ms_bufferizer_skip_bytes(&ad_->bf, avail - (ask * 2));
		}
		if (avail != 0) {
			if (ad_->minBufferFilling == -1) {
				ad_->minBufferFilling = avail;
			} else if (avail < ad_->minBufferFilling) {
				ad_->minBufferFilling = avail;
			}
		}
		int size = MIN(avail, ask);
		// info.frameCount = info.size / 2;
		if (size > 0) {
			ms_bufferizer_read(&ad_->bf, (uint8_t *)buffer, size);
		} else {
			/* we have an underrun (no more samples to deliver to the callback). We need to reset minBufferFilling*/
			ad_->minBufferFilling = -1;
		}
		ms_mutex_unlock(&ad_->mutex);
		ad_->nbufs++;
		ad_->nFramesRequested += size / 2;
		// writeBuffer(buffer, &info);
		ms_message("AudioRenderer OnWriteData end");
		return size > 0 ? 0 : -1;
	}

	int32_t OnStreamEvent(OH_AudioRenderer *renderer, OH_AudioStream_Event event) {
		(void)renderer;
		return 0;
	}

	int32_t
	OnInterruptEvent(OH_AudioRenderer *renderer, OH_AudioInterrupt_ForceType type, OH_AudioInterrupt_Hint hint) {
		(void)renderer;
		(void)hint;

		return 0;
	}

	int32_t OnError(OH_AudioRenderer *renderer, OH_AudioStream_Result error) {
		(void)renderer;
		ms_message("AudioRenderer Ohos stream callback error:%d", error);
		return 0;
	}

	int start() {
		OH_AudioStream_Result ret = OH_AudioStream_Result::AUDIOSTREAM_ERROR_SYSTEM;
		if (renderer_) {
			ret = OH_AudioRenderer_Start(renderer_);
		} else {
			initAudioRenderer();
			ret = OH_AudioRenderer_Start(renderer_);
		}
		return ret;
	}

	void stop() {
		OH_AudioRenderer_Stop(renderer_);
	}

	void destroy() {
		if (renderer_) {
			OH_AudioRenderer_Release(renderer_);
			renderer_ = nullptr;
		}
		if (builder) {
			OH_AudioStreamBuilder_Destroy(builder);
			builder = nullptr;
		}
	}

private:
	void initAudioRenderer() {
		ms_message("initAudioRenderer start");
		OH_AudioStreamBuilder *builder = nullptr;
		auto result = OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_RENDERER);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioRenderer Creating Ohos stream Builder fail");
			return;
		}
		result = OH_AudioStreamBuilder_SetSamplingRate(builder, sampleRate);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioRenderer Creating Ohos stream SetSamplingRate fail");
			return;
		}
		result = OH_AudioStreamBuilder_SetChannelCount(builder, channelCount);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioRenderer Creating Ohos stream SetChannelCount fail");
			return;
		}
		result = OH_AudioStreamBuilder_SetLatencyMode(builder, AUDIOSTREAM_LATENCY_MODE_FAST);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioRenderer Creating Ohos stream SetLatencyMode fail");
			return;
		}
		OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE);
		OH_AudioRenderer_Callbacks callbacks;
		callbacks.OH_AudioRenderer_OnWriteData = OnWriteData_1;
		callbacks.OH_AudioRenderer_OnStreamEvent = OnStreamEvent_1;
		callbacks.OH_AudioRenderer_OnInterruptEvent = OnInterruptEvent_1;
		callbacks.OH_AudioRenderer_OnError = OnError_1;
		result = OH_AudioStreamBuilder_SetRendererCallback(builder, callbacks, this);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioRenderer Creating Ohos stream SetRendererCallback fail");
			return;
		}
		OH_AudioRenderer *stream = nullptr;
		OH_AudioStreamBuilder_SetFrameSizeInCallback(builder, frameSize / 2);
		result = OH_AudioStreamBuilder_GenerateRenderer(builder, &stream);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioRenderer Creating Ohos stream GenerateRenderer fail");
			return;
		}
		renderer_ = stream;
		ms_message("initAudioRenderer success");
	}
};

/**ohos auido capture*/
class AudioCapturer {
public:
	uint32_t sampleRate;
	uint32_t channelCount;
	OH_AudioCapturer *audioCapturer{nullptr};
	OH_AudioStreamBuilder *builder{nullptr};
	std::mutex mtx_;
	std::string buffer_;
	int64_t timestamp_{0};

public:
	AudioCapturer(uint32_t rate, uint32_t count) : sampleRate(rate), channelCount(count) {
		initAudioCapt();
	}
	~AudioCapturer() {
		destroy();
	}
	int start() {
		OH_AudioStream_Result ret = OH_AudioStream_Result::AUDIOSTREAM_ERROR_SYSTEM;
		if (audioCapturer) {
			ret = OH_AudioCapturer_Start(audioCapturer);
		} else {
			initAudioCapt();
			ret = OH_AudioCapturer_Start(audioCapturer);
		}

		return ret;
	}

	void stop() {
		if (audioCapturer) {
			OH_AudioCapturer_Stop(audioCapturer);
		}
	}

	void destroy() {
		ms_message("AudioCapturer ------destroy");
		if (audioCapturer) {
			OH_AudioCapturer_Release(audioCapturer);
			audioCapturer = nullptr;
		}
		if (builder) {
			OH_AudioStreamBuilder_Destroy(builder);
			builder = nullptr;
		}
	}

	static int32_t OnReadData_1(OH_AudioCapturer *stream, void *userData, void *buffer, int32_t length) {
		AudioCapturer *self = reinterpret_cast<AudioCapturer *>(userData);
		return self->OnReadData(stream, buffer, length);
	}

	static int32_t Capt_OnError_1(OH_AudioCapturer *stream, void *userData, OH_AudioStream_Result error) {
		AudioCapturer *self = reinterpret_cast<AudioCapturer *>(userData);
		return self->OnError(stream, error);
	}

	int32_t OnError(OH_AudioCapturer *stream, OH_AudioStream_Result error) {
		(void)stream;
		ms_message("AudioCapturer Ohos stream callback error:%d", error);
		return 0;
	}

	// 函数用于将void *数组转换为int16_t数组
	void convertVoidToInt16(void *buffer, int bufferSize, int16_t **result, size_t *resultSize) {
		*resultSize = bufferSize / sizeof(int16_t);
		*result = (int16_t *)malloc(*resultSize * sizeof(int16_t));
		if (*result == NULL) {
			return;
		}
		// 使用memcpy进行内存复制
		memcpy(*result, buffer, bufferSize);
	}

	void GetBuffer(MSQueue *output) {
		std::unique_lock<std::mutex> lock(mtx_);
		if (buffer_.empty()) {
			return;
		}
		mblk_t *m = allocb(buffer_.size(), 0);
		if (!m) return;
		memcpy(m->b_wptr, buffer_.c_str(), buffer_.size());
		m->b_wptr += buffer_.size();

		mblk_set_timestamp_info(m, timestamp_);
		ms_queue_put(output, m);
	}

	int32_t OnReadData(OH_AudioCapturer *renderer, void *buffer, int32_t length) {
		(void)renderer;
		int64_t position;
		std::unique_lock<std::mutex> lock(mtx_);
		auto result = OH_AudioCapturer_GetTimestamp(audioCapturer, CLOCK_MONOTONIC, &position, &timestamp_);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioCapturer Ohos stream GetTimestamp fail");
			return -1;
		}
		ms_message("buffer_ size: %d", length);
		buffer_ = std::string((char *)buffer, length);
		return 0;
	}

private:
	void initAudioCapt() {
		ms_message("initAudioCapt start,audioCapturer:%p", audioCapturer);
		if (audioCapturer) {
			destroy();
		}

		auto result = OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_CAPTURER);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioCapturer Creating Ohos stream Builder fail");
			return;
		}
		result = OH_AudioStreamBuilder_SetSamplingRate(builder, sampleRate);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioCapturer Creating Ohos stream SetSamplingRate fail");
			return;
		}
		result = OH_AudioStreamBuilder_SetChannelCount(builder, channelCount);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioCapturer Creating Ohos stream SetChannelCount fail");
			return;
		}
		result = OH_AudioStreamBuilder_SetLatencyMode(builder, AUDIOSTREAM_LATENCY_MODE_NORMAL);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("AudioCapturer Creating Ohos stream SetLatencyMode fail");
			return;
		}
		result = OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("OH_AudioStreamBuilder_SetSampleFormat fail");
			return;
		}
		result = OH_AudioStreamBuilder_SetEncodingType(builder, AUDIOSTREAM_ENCODING_TYPE_RAW);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("OH_AudioStreamBuilder_SetEncodingType fail");
			return;
		}
		OH_AudioCapturer_Callbacks callbacks;
		memset(&callbacks, 0, sizeof(OH_AudioCapturer_Callbacks));
		callbacks.OH_AudioCapturer_OnReadData = OnReadData_1;
		callbacks.OH_AudioCapturer_OnError = Capt_OnError_1;
		callbacks.OH_AudioCapturer_OnInterruptEvent = nullptr;
		callbacks.OH_AudioCapturer_OnStreamEvent = nullptr;
		result = OH_AudioStreamBuilder_SetCapturerCallback(builder, callbacks, this);
		if (result != AUDIOSTREAM_SUCCESS) {
			ms_message("OH_AudioStreamBuilder_SetCapturerCallback fail");
			return;
		}

		// OH_AudioStreamBuilder_SetFrameSizeInCallback(builder,1920);
		OH_AudioCapturer *stream = nullptr;
		result = OH_AudioStreamBuilder_GenerateCapturer(builder, &stream);
		if (stream == nullptr) {
		}
		audioCapturer = stream;
		ms_message("initAudioCapt success");
	}
};

typedef struct _AlawEncData {
	MSBufferizer *bz;
	int ptime;
	int maxptime;
	uint32_t ts;

	AudioCapturer *rec_engine;
	uint64_t flowControlStart;
} AlawEncData;

static AlawEncData *alaw_enc_data_new(void) {
	AlawEncData *obj = (AlawEncData *)ms_new0(AlawEncData, 1);
	obj->bz = ms_bufferizer_new();
	obj->ptime = 0;
	obj->maxptime = MIN(MS_DEFAULT_MAX_PTIME, 140);
	obj->ts = 0;

	std::vector<AudioDeviceInfo> devices = AudioDeviceManager::GetDevices();
	if (devices.empty()) {
		return nullptr;
	}

	obj->rec_engine = new AudioCapturer(16000, 1);
	obj->rec_engine->start();

	return obj;
}

static void alaw_enc_data_destroy(AlawEncData *obj) {
	ms_bufferizer_destroy(obj->bz);
	ms_free(obj);
}

static void alaw_enc_init(MSFilter *obj) {
	ms_message("alaw_enc_init");
	obj->data = alaw_enc_data_new();
}

static void alaw_enc_uninit(MSFilter *obj) {
	AlawEncData *dt = (AlawEncData *)obj->data;
	dt->rec_engine->stop();
	delete dt->rec_engine;

	alaw_enc_data_destroy((AlawEncData *)obj->data);
}

static void alaw_enc_process(MSFilter *obj) {
	AlawEncData *dt = (AlawEncData *)obj->data;


	dt->rec_engine->GetBuffer(obj->outputs[0]);
}

static int enc_add_attr(MSFilter *f, void *arg) {
	const char *fmtp = (const char *)arg;
	AlawEncData *s = (AlawEncData *)f->data;
	if (strstr(fmtp, "ptime:10") != NULL) {
		s->ptime = 10;
	} else if (strstr(fmtp, "ptime:20") != NULL) {
		s->ptime = 20;
	} else if (strstr(fmtp, "ptime:30") != NULL) {
		s->ptime = 30;
	} else if (strstr(fmtp, "ptime:40") != NULL) {
		s->ptime = 40;
	} else if (strstr(fmtp, "ptime:50") != NULL) {
		s->ptime = 50;
	} else if (strstr(fmtp, "ptime:60") != NULL) {
		s->ptime = 60;
	} else if (strstr(fmtp, "ptime:70") != NULL) {
		s->ptime = 70;
	} else if (strstr(fmtp, "ptime:80") != NULL) {
		s->ptime = 80;
	} else if (strstr(fmtp, "ptime:90") != NULL) {
		s->ptime = 90;
	} else if (strstr(fmtp, "ptime:100") != NULL) {
		s->ptime = 100;
	} else if (strstr(fmtp, "ptime:110") != NULL) {
		s->ptime = 110;
	} else if (strstr(fmtp, "ptime:120") != NULL) {
		s->ptime = 120;
	} else if (strstr(fmtp, "ptime:130") != NULL) {
		s->ptime = 130;
	} else if (strstr(fmtp, "ptime:140") != NULL) {
		s->ptime = 140;
	}
	return 0;
}

static int enc_add_fmtp(MSFilter *f, void *arg) {
	const char *fmtp = (const char *)arg;
	AlawEncData *s = (AlawEncData *)f->data;
	char tmp[30];
	if (fmtp_get_value(fmtp, "maxptime", tmp, sizeof(tmp))) {
		s->maxptime = MIN(atoi(tmp), MS_DEFAULT_MAX_PTIME);
	}
	if (fmtp_get_value(fmtp, "ptime", tmp, sizeof(tmp))) {
		ms_message("%s configured with ptime=%s", f->desc->name, tmp);
		s->ptime = MIN(atoi(tmp), s->maxptime);
		if (s->ptime == s->maxptime) ms_message("%s ptime set to maxptime=%i", f->desc->name, s->maxptime);
	}
	return 0;
}

static int get_channels(BCTBX_UNUSED(MSFilter *f), void *arg) {
	*((int *)arg) = 1;
	return 0;
}

static int get_sample_rate(BCTBX_UNUSED(MSFilter *f), void *arg) {
	*((int *)arg) = 16000;
	return 0;
}

static int get_ptime(MSFilter *f, void *arg) {
	AlawEncData *s = (AlawEncData *)f->data;
	*((int *)arg) = s->ptime;
	return 0;
}

static MSFilterMethod enc_methods[] = {
    {MS_FILTER_ADD_ATTR, enc_add_attr},      {MS_FILTER_ADD_FMTP, enc_add_fmtp},
    {MS_FILTER_GET_NCHANNELS, get_channels}, {MS_FILTER_GET_SAMPLE_RATE, get_sample_rate},
    {MS_AUDIO_ENCODER_GET_PTIME, get_ptime}, {0, NULL}};

MSFilterDesc ms_ohos_audio_enc_desc = {.id = MS_OHOS_AUDIO_ENC,
                                       .name = "OHOSAudioEncode",
                                       .text = N_("ohos audio encoder"),
                                       .category = MS_FILTER_ENCODING_CAPTURER,
                                       .enc_fmt = "pcm",
                                       .ninputs = 0,
                                       .noutputs = 1,
                                       .init = alaw_enc_init,
                                       .process = alaw_enc_process,
                                       .uninit = alaw_enc_uninit,
                                       .methods = enc_methods};

MS_FILTER_DESC_EXPORT(ms_ohos_audio_enc_desc)

extern "C" {
#include "g711.h"
}

// ================================================decoder========================================

static const int flowControlIntervalMs = 1000;
static const int flowControlThresholdMs = 40;

static void ohos_dec_init(MSFilter *obj) {
	ms_message("ohos_dec_init");
	OHOSAudioWriteData *ad = new OHOSAudioWriteData();
	std::vector<AudioDeviceInfo> devices = AudioDeviceManager::GetDevices();
	if (devices.empty()) {
		return;
	}
	uint32_t frameSize = 48000 * 20 / 1000 * 16 / 8;
	ad->tr = new AudioRenderer(48000, devices[2].outputCount, frameSize, ad);
	obj->data = ad;
}

static void ohos_dec_uninit(MSFilter *obj) {
	ms_message("%s: %d", __FILE__, __LINE__);
	OHOSAudioWriteData *ad = (OHOSAudioWriteData *)obj->data;
	delete ad;
}

static void ohos_dec_preprocess(MSFilter *obj) {
	OHOSAudioWriteData *ad = (OHOSAudioWriteData *)obj->data;
	// int play_buf_size = (1024 * (ad->rate)) / 8000;
	// int notify_frames = (int)(0.01* (float)ad->rate);
	ad->nFramesRequested = 0;
	ad->updateStreamTypeFromMsSndCard();
	ad->nbufs = 0;
	ad->mStarted = false;
	ad->flowControlStart = obj->ticker->time;
	ad->minBufferFilling = -1;
}

static int bytes_to_ms(OHOSAudioWriteData *ad, int bytes) {
	return bytes * 1000 / (2 * ad->nchannels * ad->rate);
}

static void ohos_dec_process(MSFilter *obj) {
	OHOSAudioWriteData *ad = (OHOSAudioWriteData *)obj->data;
	if (!ad->tr) {
		ms_queue_flush(obj->inputs[0]);
		return;
	}
	if (!ad->mStarted) ad->tr->start();
	ms_mutex_lock(&ad->mutex);
	ms_bufferizer_put_from_queue(&ad->bf, obj->inputs[0]);

	if (((uint32_t)(obj->ticker->time - ad->flowControlStart)) >= flowControlIntervalMs) {
		int threshold = (flowControlThresholdMs * ad->nchannels * 2 * ad->rate) / 1000;
		// ms_message("Time to flow control: minBufferFilling=%i, threshold=%i",ad->minBufferFilling, threshold);
		if (ad->minBufferFilling > threshold) {
			int drop = ad->minBufferFilling -
			           (threshold / 4); // keep a bit in order not to risk an underrun in the next period.
			ms_warning(
			    "Too many samples waiting in sound writer (minBufferFilling=%i ms, threshold=%i ms), dropping %i ms",
			    bytes_to_ms(ad, ad->minBufferFilling), bytes_to_ms(ad, threshold), bytes_to_ms(ad, drop));
			ms_bufferizer_skip_bytes(&ad->bf, drop);
		}
		ad->flowControlStart = obj->ticker->time;
		ad->minBufferFilling = -1;
	}
	ms_mutex_unlock(&ad->mutex);
	// AlawEncData *s = (AlawEncData *)obj->data;
}

static void ohos_dec_postprocess(MSFilter *obj) {
	ms_message("%s: %d", __FILE__, __LINE__);
	OHOSAudioWriteData *ad = (OHOSAudioWriteData *)obj->data;
	if (!ad->tr) return;
	ms_message("Stopping sound playback");
	ad->tr->stop();
	ms_message("Sound playback stopped");
	ms_message("Sound playback flushed, deleting");
	ad->tr = NULL;
	ad->mStarted = false;
}

static int get_sample_write_rate(BCTBX_UNUSED(MSFilter *f), void *arg) {
	*((int *)arg) = 48000;
	return 0;
}

static int set_sample_write_rate(MSFilter *obj, void *data) {
	return -1;
}

static int ohos_write_set_nchannels(MSFilter *obj, void *data) {
	int *n = (int *)data;
	OHOSAudioWriteData *ad = (OHOSAudioWriteData *)obj->data;
	ad->nchannels = *n;
	return 0;
}

static int ohos_write_get_nchannels(MSFilter *obj, void *data) {
	int *n = (int *)data;
	OHOSAudioWriteData *ad = (OHOSAudioWriteData *)obj->data;
	*n = ad->nchannels;
	return 0;
}

static int dec_have_plc(BCTBX_UNUSED(MSFilter *f), void *arg) {
	*((int *)arg) = 0;
	return 0;
}

// static MSFilterMethod dec_methods[] = {
//     {MS_FILTER_GET_NCHANNELS, get_channels},
//     {MS_FILTER_GET_SAMPLE_RATE, get_sample_write_rate},
//     {MS_DECODER_HAVE_PLC, dec_have_plc},
//     {0, NULL}
// };

static MSFilterMethod dec_methods[] = {{MS_FILTER_SET_SAMPLE_RATE, set_sample_write_rate},
                                       {MS_FILTER_GET_SAMPLE_RATE, get_sample_write_rate},
                                       {MS_FILTER_SET_NCHANNELS, ohos_write_set_nchannels},
                                       {MS_FILTER_GET_NCHANNELS, ohos_write_get_nchannels},
                                       {MS_DECODER_HAVE_PLC, dec_have_plc},
                                       {0, NULL}};

// MS_FILTER_DECODER_RENDERER
MSFilterDesc ms_ohos_audio_dec_desc = {.id = MS_OHOS_AUDIO_DEC,
                                       .name = "OHOSAudioDecode",
                                       .text = N_("ohos audio decoder"),
                                       .category = MS_FILTER_OTHER,
                                       .enc_fmt = "pcmu",
                                       .ninputs = 1,
                                       .noutputs = 0,
                                       .init = ohos_dec_init,
                                       .preprocess = ohos_dec_preprocess,
                                       .process = ohos_dec_process,
                                       .postprocess = ohos_dec_postprocess,
                                       .uninit = ohos_dec_uninit,
                                       .methods = dec_methods};

MS_FILTER_DESC_EXPORT(ms_ohos_audio_dec_desc)

#include "mediastreamer2/mssndcard.h"

// 虚拟设备打开 reader（输入设备）
MSFilter *ohos_create_reader(MSSndCard *card) {
	ms_message("OHOS create_reader");
	MSFilter *f = ms_factory_create_filter_from_desc(ms_snd_card_get_factory(card), &ms_ohos_audio_enc_desc);
	return f;
}

// 虚拟设备打开 writer（输出设备）
static MSFilter *ohos_create_writer(MSSndCard *card) {
	ms_message("OHOS create_writer");
	MSFilter *f = ms_factory_create_filter_from_desc(ms_snd_card_get_factory(card), &ms_ohos_audio_dec_desc);
	return f;
}

// 初始化和反初始化（可选）
static void ohos_card_init(MSSndCard *card) {
	ms_message("OHOS sound card init.");
}
static void ohos_card_uninit(MSSndCard *card) {
	ms_message("OHOS sound card uninit.");
}

static void ohos_snd_card_detect(MSSndCardManager *m);

// 定义 MSSndCardDesc（新版结构）
MSSndCardDesc ohos_snd_card_desc = {.driver_type = "OHOSVirtualAudio",
                                    .detect = ohos_snd_card_detect,
                                    .init = ohos_card_init,
                                    .set_level = NULL,
                                    .get_level = NULL,
                                    .set_capture = NULL,
                                    .set_control = NULL,
                                    .get_control = NULL,
                                    .create_reader = ohos_create_reader,
                                    .create_writer = ohos_create_writer,
                                    .uninit = ohos_card_uninit,
                                    .duplicate = NULL,
                                    .unload = NULL,
                                    .usage_hint = NULL,
                                    .audio_session_activated = NULL,
                                    .callkit_enabled = NULL,
                                    .audio_route_changed = NULL,
                                    .configure = NULL,
                                    .reload_requested = NULL};

static MSSndCard *ohos_snd_card_new(SoundDeviceDescription *d) {
	MSSndCard *obj;

	obj = ms_snd_card_new(&ohos_snd_card_desc);
	obj->name = ms_strdup("OHOSVirtualAudio");
	obj->device_description = d;

	if (d->flags & DEVICE_HAS_BUILTIN_AEC) obj->capabilities |= MS_SND_CARD_CAP_BUILTIN_ECHO_CANCELLER;
	obj->latency = d->delay;
	return obj;
}

static void ohos_snd_card_detect(MSSndCardManager *m) {
	SoundDeviceDescription *d = NULL;
	MSDevicesInfo *devices = NULL;

	devices = ms_factory_get_devices_info(m->factory);
	d = ms_devices_info_get_sound_device_description(devices);

	MSSndCard *card = ohos_snd_card_new(d);
	ms_snd_card_set_manager(m, card);
	ms_snd_card_manager_add_card(m, card);
}
